﻿using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Configuration;
using Microsoft.IdentityModel.Tokens;
using RuoVea.ExDto;
using RuoVea.ExIdGen;
using RuoVea.ExUtil;
using System;
using System.Collections.Generic;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Security.Cryptography;
using System.Security.Principal;
using System.Text;

namespace RuoVea.ExWeb.Jwt
{
    /// <summary>
    /// 
    /// </summary>
    public class JWTEncryption
    {
        #region 参数校验
        /// <summary>
        /// 参数校验
        /// </summary>
        /// <param name="iss"></param>
        /// <param name="aud"></param>
        /// <param name="secret"></param>
        static void Volatile(string iss, string aud, string secret)
        {
            if (string.IsNullOrWhiteSpace(aud))
                throw new ArgumentNullException("aud不能为空.");

            if (string.IsNullOrWhiteSpace(iss))
                throw new ArgumentNullException("iss不能为空.");

            if (string.IsNullOrWhiteSpace(secret))
                throw new ArgumentNullException("secret不能为空.");
        }
        #endregion

        #region Volatile
        static void Volatile(string iss)
        {
            if (string.IsNullOrWhiteSpace(iss))
                throw new ArgumentNullException("iss不能为空.");
        }
        #endregion

        #region 生成 Token
        /// <summary>
        /// 生成 Token
        /// </summary>
        /// <param name="payload"></param>
        /// <param name="expiredTime">过期时间（分钟）</param>
        /// <returns></returns>
        public static string Encrypt(TokenEntity payload, long? expiredTime = null)
        {
            var dic = new Dictionary<string, object>();

            if (payload.UserId.IsNullOrWhiteSpace())
                throw new Exception("UserId必填");

            if (payload.UserId.IsNullOrWhiteSpace())
                throw new Exception("Account必填");

            dic.Add(ClaimConst.CLAINM_USERID, payload.UserId);

            dic.Add(ClaimConst.CLAINM_ACCOUNT, payload.Account);

            if (payload.TenantId.NotNullOrWhiteSpace())
                dic.Add(ClaimConst.TENANT_ID, payload.TenantId);

            if (payload.Name.NotNullOrWhiteSpace())
                dic.Add(ClaimConst.CLAINM_NAME, payload.Name);

            if (payload.IsAdmin.NotNullOrWhiteSpace())
                dic.Add(ClaimConst.CLAINM_ISADMIN, payload.IsAdmin);

            if (payload.RoleIds.NotNullOrWhiteSpace())
                dic.Add(ClaimConst.CLAINM_ROLEIDS, payload.RoleIds);

            var accessToken = Encrypt(dic, payload);

            return accessToken;
        }
        #endregion

        #region 生成 Token
        /// <summary>
        /// 生成 Token
        /// </summary>
        /// <param name="payload"></param>
        /// <param name="tokenData"></param>
        /// <param name="expiredTime">过期时间（分钟）</param>
        /// <returns></returns>
        private static string Encrypt(IDictionary<string, object> payload, TokenEntity tokenData, long? expiredTime = null)
        {
            var (Payload, JWTSettings, expTime) = CombinePayload(payload, expiredTime);
            return Encrypt(JWTSettings.IssuerSigningKey, Payload, tokenData, JWTSettings, expTime, JWTSettings.Algorithm);
        }
        #endregion

        #region 生成 Token
        private static object Encrypt(IDictionary<string, object> payload, long? expiredTime = null)
        {
            var (Payload, JWTSettings, expTime) = CombinePayload(payload, expiredTime);
            return Encrypt(JWTSettings.IssuerSigningKey, Payload, JWTSettings, expTime, JWTSettings.Algorithm);
        }
        #endregion

        #region 颁发JWT字符串
        /// <summary>
        /// 颁发JWT字符串
        /// </summary>
        /// <param name="issuerSigningKey"></param>
        /// <param name="payload"></param>
        /// <param name="tokenData"></param>
        /// <param name="jwtSettings"></param>
        /// <param name="expire"></param>
        /// <param name="algorithm"></param>
        /// <returns></returns>
        private static string Encrypt(string issuerSigningKey, List<Claim> payload, TokenEntity tokenData, JWTSettingsOptions jwtSettings, long expire, string algorithm = SecurityAlgorithms.HmacSha256)
        {
            string iss = jwtSettings.ValidIssuer;// Appsettings.app("JwtSetting", "Issuer").ToString();
            string aud = jwtSettings.ValidAudience;// Appsettings.app("JwtSetting", "Audience").ToString();
            string secret = jwtSettings.IssuerSigningKey;// Appsettings.app("JwtSetting", "SecretKey").ToString();

            var identity = new ClaimsIdentity(new GenericIdentity(tokenData.UserId, "Token"));

            identity.AddClaim(new Claim(ClaimTypes.Name, tokenData.UserId));
            identity.AddClaim(new Claim(JwtRegisteredClaimNames.Sub, tokenData.Account));
            identity.AddClaim(new Claim(JwtRegisteredClaimNames.Jti, IdGenerator.Id.ToString()));
            identity.AddClaim(new Claim(ClaimTypes.Role, tokenData.RoleIds));
            payload.Add(identity.FindFirst(ClaimTypes.Name));

            // 过期时间
            DateTime IssuedAt = DateTime.Now;
            //秘钥 (SymmetricSecurityKey 对安全性的要求，密钥的长度太短会报出异常)
            var securityKey = new SigningCredentials(new SymmetricSecurityKey(Encoding.UTF8.GetBytes(secret)), SecurityAlgorithms.HmacSha256);
            var jwt = new JwtSecurityToken(
                issuer: iss,
                audience: "audience",
                claims: payload,
                notBefore: IssuedAt,
                expires: IssuedAt.Add(TimeSpan.FromSeconds(expire)),
                signingCredentials: securityKey
                );

            var encodedJwt = new JwtSecurityTokenHandler().WriteToken(jwt);
            return encodedJwt;
        }

        private static object Encrypt(string issuerSigningKey, List<Claim> payload, JWTSettingsOptions jwtSettings, long expire, string algorithm = SecurityAlgorithms.HmacSha256)
        {
            string iss = jwtSettings.ValidIssuer;// Appsettings.app("JwtSetting", "Issuer").ToString();
            string aud = jwtSettings.ValidAudience;// Appsettings.app("JwtSetting", "Audience").ToString();
            string secret = jwtSettings.IssuerSigningKey;// Appsettings.app("JwtSetting", "SecretKey").ToString();


            // 过期时间
            DateTime IssuedAt = DateTime.Now;
            //秘钥 (SymmetricSecurityKey 对安全性的要求，密钥的长度太短会报出异常)
            var securityKey = new SigningCredentials(new SymmetricSecurityKey(Encoding.UTF8.GetBytes(secret)), SecurityAlgorithms.HmacSha256);
            var jwt = new JwtSecurityToken(
                issuer: iss,
                audience: "audience",
                claims: payload,
                notBefore: IssuedAt,
                expires: IssuedAt.Add(TimeSpan.FromSeconds(expire)),
                signingCredentials: securityKey
                );

            var encodedJwt = new JwtSecurityTokenHandler().WriteToken(jwt);
            var response = new
            {
                auth_token = encodedJwt,
                expires_in = expire
            };

            return response;
        }
        #endregion

        #region 解析 jwt
        /// <summary>
        /// 解析
        /// </summary>
        /// <param name="jwtStr"></param>
        /// <returns></returns>
        public static TokenEntity SerializeJwt(string jwtStr)
        {
            JWTSettingsOptions jwtSettings = GetJWTSettings();

            var jwtHandler = new JwtSecurityTokenHandler();

            if (!jwtHandler.CanReadToken(jwtStr))
            {
                return null;
            }

            string iss = jwtSettings.ValidIssuer;// Appsettings.app("JwtSetting", "Issuer").ToString();
            Volatile(iss);

            JwtSecurityToken jwtToken = jwtHandler.ReadJwtToken(jwtStr);
            if (jwtToken.Issuer != iss)
            {
                return null;//不正确
            }
            if (jwtToken.ValidTo < DateTime.UtcNow)
            {
                return null;//过期
            }

            object userId;
            object account;
            object name;
            object roleIds;
            object isAdmin;
            object tenantId;

            try
            {
                jwtToken.Payload.TryGetValue(ClaimConst.CLAINM_USERID, out userId);
                jwtToken.Payload.TryGetValue(ClaimConst.CLAINM_ACCOUNT, out account);
                jwtToken.Payload.TryGetValue(ClaimConst.CLAINM_NAME, out name);
                jwtToken.Payload.TryGetValue(ClaimConst.CLAINM_ROLEIDS, out roleIds);
                jwtToken.Payload.TryGetValue(ClaimConst.CLAINM_ISADMIN, out isAdmin);
                jwtToken.Payload.TryGetValue(ClaimConst.TENANT_ID, out tenantId);
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex);
                throw;
            }
            var tm = new TokenEntity
            {
                UserId = userId?.ToString(),
                Account = account?.ToString(),
                Name = name?.ToString(),
                IsAdmin = isAdmin?.ToString(),
                TenantId = tenantId?.ToString(),
                RoleIds = roleIds != null ? roleIds.ToString() : "",
            };
            return tm;
        }
        #endregion

        #region 时间差
        /// <summary>
        /// 时间差
        /// </summary>
        /// <param name="date"></param>
        /// <returns>Date converted to seconds since Unix epoch (Jan 1, 1970, midnight UTC).</returns>    
        public static long ToUnixEpochDate(DateTime date)
        {
            return (long)Math.Round((date.ToUniversalTime() - new DateTimeOffset(1970, 1, 1, 0, 0, 0, TimeSpan.Zero)).TotalSeconds);
        }
        #endregion

        #region 获取过期时间
        /// <summary>
        /// 获取过期时间
        /// </summary>
        /// <param name="jwtStr"></param>
        /// <returns></returns>
        public static DateTime GetExp(string jwtStr)
        {
            var jwtHandler = new JwtSecurityTokenHandler();
            JwtSecurityToken jwtToken = jwtHandler.ReadJwtToken(jwtStr);

            DateTime expDate = (jwtToken.Payload[JwtRegisteredClaimNames.Exp] ?? 0).ToInt().ToDate();
            return expDate;
        }
        #endregion

        #region 时间是否有效
        /// <summary>
        /// 时间是否有效
        /// </summary>
        /// <param name="jwtStr"></param>
        /// <returns></returns>
        public static bool IsExp(string jwtStr)
        {
            return GetExp(jwtStr) < DateTime.Now;
        }
        #endregion

        static IConfiguration Configuration;

        #region setting
        /// <summary>
        /// 
        /// </summary>
        /// <param name="configuration"></param>
        public static void setting(IConfiguration configuration)
        {
            Configuration = configuration;
        }
        #endregion

        #region 组合 Claims 负荷 CombinePayload
        /// <summary>
        /// 组合 Claims 负荷
        /// </summary>
        /// <param name="payload"></param>
        /// <param name="expiredTime">过期时间，单位：分钟</param>
        /// <returns></returns>
        private static (List<Claim> Payload, JWTSettingsOptions JWTSettings, long expTime) CombinePayload(IDictionary<string, object> payload, long? expiredTime = null)
        {
            JWTSettingsOptions jwtSettings = GetJWTSettings();
            var claims = new List<Claim>();
            long expTime = 0;

            foreach (var item in payload)
            {
                if (item.Value != null)
                    claims.Add(new Claim(item.Key.ToString(), item.Value?.ToString() ?? ""));
            }

            var datetimeOffset = DateTimeOffset.UtcNow;

            if (!payload.ContainsKey(JwtRegisteredClaimNames.Iat))
            {
                claims.Add(new Claim(JwtRegisteredClaimNames.Iat, datetimeOffset.ToUnixTimeSeconds().ToString()));
            }

            if (!payload.ContainsKey(JwtRegisteredClaimNames.Nbf))
            {
                claims.Add(new Claim(JwtRegisteredClaimNames.Nbf, datetimeOffset.ToUnixTimeSeconds().ToString()));
            }

            if (!payload.ContainsKey(JwtRegisteredClaimNames.Exp))
            {
                expTime = expiredTime ?? jwtSettings?.ExpiredTime ?? 20;
                claims.Add(new Claim(JwtRegisteredClaimNames.Exp, DateTimeOffset.UtcNow.AddMinutes(expTime).ToUnixTimeSeconds().ToString()));
            }

            if (!payload.ContainsKey(JwtRegisteredClaimNames.Iss))
            {
                claims.Add(new Claim(JwtRegisteredClaimNames.Iss, jwtSettings?.ValidIssuer));
            }

            if (!payload.ContainsKey(JwtRegisteredClaimNames.Aud))
            {
                claims.Add(new Claim(JwtRegisteredClaimNames.Aud, jwtSettings?.ValidAudience));
            }

            return (claims, jwtSettings, expTime);
        }
        #endregion

        #region 获取 JWT 配置
        /// <summary>
        /// 获取 JWT 配置
        /// </summary>
        /// <returns></returns>
        public static JWTSettingsOptions GetJWTSettings()
        {
            var setting = Configuration.GetSection("JWTSettings").Get<JWTSettingsOptions>();
            if ((setting == null) || string.IsNullOrWhiteSpace(setting.IssuerSigningKey))
                return SetDefaultJwtSettings(new JWTSettingsOptions());
            else return setting;
        }
        #endregion

        #region 生成刷新 Token
        /// <summary>
        /// 生成刷新 Token
        /// </summary>
        /// <param name="accessToken"></param>
        /// <param name="expiredTime">刷新 Token 有效期（分钟）</param>
        /// <returns></returns>
        public static string GenerateRefreshToken(string accessToken, int expiredTime = 43200)
        {
            // 分割Token
            var tokenParagraphs = accessToken.Split('.', StringSplitOptions.RemoveEmptyEntries);

            var s = RandomNumberGenerator.GetInt32(10, tokenParagraphs[1].Length / 2 + 2);
            var l = RandomNumberGenerator.GetInt32(3, 13);

            var payload = new Dictionary<string, object>
            {
                { "f",tokenParagraphs[0] },
                { "e",tokenParagraphs[2] },
                { "s",s },
                { "l",l },
                { "k",tokenParagraphs[1].Substring(s,l) }
            };

            return Encrypt(payload, expiredTime)?.ToString();
        }
        #endregion

        #region 外部处理缓存的问题

        /// <summary>
        /// 通过过期Token 和 刷新Token 换取新的 Token
        /// </summary>
        /// <param name="expiredToken"></param>
        /// <param name="refreshToken"></param>
        /// <param name="beforFun">判断这个刷新Token 是否已刷新过 处理token并发容错问题 返回cacheValue  示例: ExCache.CacheFactery.CaChe.Read<string>(blacklistRefreshKey) </param>
        /// <param name="afterFun">交换成功后登记刷新Token，标记失效  示例: ExCache.CacheFactery.CaChe.Write(blacklistRefreshKey, refreshTokenObj, absoluteExpiration.TimeOfDay)</param>
        /// <param name="expiredTime">过期时间（分钟）</param>
        /// <param name="clockSkew">刷新token容差值，秒做单位</param>
        /// <returns></returns>
        public static string Exchange(string expiredToken, string refreshToken, Func<string, string> beforFun, Action<string, string, DateTimeOffset> afterFun, long? expiredTime = null, long clockSkew = 5)
        {
            // 交换刷新Token 必须原Token 已过期
            var (_isValid, _, _) = Validate(expiredToken);
            if (_isValid) return default;

            // 判断刷新Token 是否过期
            var (isValid, refreshTokenObj, refreshResult) = Validate(refreshToken);
            if (!isValid) return default;

            // 判断这个刷新Token 是否已刷新过
            var blacklistRefreshKey = "BLACKLIST_REFRESH_TOKEN:" + refreshToken;
            // 处理token并发容错问题
            DateTimeOffset nowTime = DateTimeOffset.UtcNow;

            string cachedValue = string.Empty;// ExCache.CacheFactery.CaChe.Read<string>(blacklistRefreshKey);
            cachedValue = beforFun(blacklistRefreshKey);
            bool isRefresh = !string.IsNullOrWhiteSpace(cachedValue);    // 判断是否刷新过
            if (isRefresh)
            {
                DateTimeOffset refreshTime = new(long.Parse(cachedValue), TimeSpan.Zero);
                // 处理并发时容差值
                if ((nowTime - refreshTime).TotalSeconds > clockSkew) return default;
            }

            // 分割过期Token
            var tokenParagraphs = expiredToken.Split('.', StringSplitOptions.RemoveEmptyEntries);
            if (tokenParagraphs.Length < 3) return default;


            // 判断各个部分是否匹配
            if (!refreshResult.FindFirstValue("f").Equals(tokenParagraphs[0])) return default;
            if (!refreshResult.FindFirstValue("e").Equals(tokenParagraphs[2])) return default;
            if (!tokenParagraphs[1].Substring(refreshResult.FindFirstValue("s").ToInt(),
                refreshResult.FindFirstValue("l").ToInt()).Equals(refreshResult.FindFirstValue("k"))) return default;

            // 获取过期 Token 的存储信息
            TokenEntity payload = SerializeJwt(expiredToken);
            string newtoken = Encrypt(payload, expiredTime);
            //// 交换成功后登记刷新Token，标记失效
            if (!isRefresh)
            {
                var absoluteExpiration = DateTimeOffset.FromUnixTimeSeconds(refreshResult.FindFirstValue(JwtRegisteredClaimNames.Exp).ToInt());
                afterFun(blacklistRefreshKey, newtoken, absoluteExpiration);
            }
            return newtoken;
        }

        #endregion

        #region 通过过期Token 和 刷新Token 换取新的 Token
        /// <summary>
        /// 通过过期Token 和 刷新Token 换取新的 Token
        /// </summary>
        /// <param name="expiredToken"></param>
        /// <param name="refreshToken"></param>
        /// <param name="expiredTime">过期时间（分钟）</param>
        /// <param name="clockSkew">刷新token容差值，秒做单位</param>
        /// <returns></returns>
        public static object Exchange(string expiredToken, string refreshToken, long? expiredTime = null, long clockSkew = 5)
        {
            // 交换刷新Token 必须原Token 已过期
            var (_isValid, _, _) = Validate(expiredToken);
            if (_isValid) return default;

            // 判断刷新Token 是否过期
            var (isValid, refreshTokenObj, refreshResult) = Validate(refreshToken);
            if (!isValid) return default;

            // 分割过期Token
            var tokenParagraphs = expiredToken.Split('.', StringSplitOptions.RemoveEmptyEntries);
            if (tokenParagraphs.Length < 3) return default;

            // 判断各个部分是否匹配
            if (!refreshResult.FindFirstValue("f").Equals(tokenParagraphs[0])) return default;
            if (!refreshResult.FindFirstValue("e").Equals(tokenParagraphs[2])) return default;
            if (!tokenParagraphs[1].Substring(refreshResult.FindFirstValue("s").ToInt(),
                refreshResult.FindFirstValue("l").ToInt()).Equals(refreshResult.FindFirstValue("k"))) return default;

            // 获取过期 Token 的存储信息
            TokenEntity payload = SerializeJwt(expiredToken);
            string newtoken = Encrypt(payload, expiredTime);
            return newtoken;
        }
        #endregion

        #region 验证 Token
        /// <summary>
        /// 验证 Token
        /// </summary>
        /// <param name="accessToken"></param>
        /// <returns></returns>
        public static (bool IsValid, SecurityToken Token, ClaimsPrincipal validationResult) Validate(string accessToken)
        {
            JWTSettingsOptions jwtSettings = GetJWTSettings();
            if (jwtSettings == null) return (false, default, default);

            // 加密Key
            SecurityKey key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtSettings.IssuerSigningKey));
            var creds = new SigningCredentials(key, jwtSettings.Algorithm ?? SecurityAlgorithms.HmacSha256);

            // 创建Token验证参数
            var tokenValidationParameters = CreateTokenValidationParameters(jwtSettings);
            if (tokenValidationParameters.IssuerSigningKey == null) tokenValidationParameters.IssuerSigningKey = creds.Key;
            // 验证 Token
            var tokenHandler = new JwtSecurityTokenHandler();
            try
            {
                SecurityToken validatedToken;
                ClaimsPrincipal tokenValidationResult = tokenHandler.ValidateToken(accessToken, tokenValidationParameters, out validatedToken);

                return (true, validatedToken, tokenValidationResult);
            }
            catch
            {
                return (false, default, default);
            }
        }
        #endregion

        #region 验证 Token
        /// <summary>
        /// 验证 Token
        /// </summary>
        /// <param name="httpContext"></param>
        /// <param name="token"></param>
        /// <param name="headerKey"></param>
        /// <param name="tokenPrefix"></param>
        /// <returns></returns>
        public static bool ValidateJwtBearerToken(DefaultHttpContext httpContext, out SecurityToken token, string headerKey = "Authorization", string tokenPrefix = "Bearer ")
        {
            // 获取 token
            var accessToken = GetJwtBearerToken(httpContext, headerKey, tokenPrefix);
            if (string.IsNullOrWhiteSpace(accessToken))
            {
                token = null;
                return false;
            }
            // 验证token
            var (IsValid, Token, _) = Validate(accessToken);
            token = IsValid ? Token : null;

            return IsValid;
        }
        #endregion

        #region 读取 Token，不含验证
        /// <summary>
        /// 读取 Token，不含验证
        /// </summary>
        /// <param name="accessToken"></param>
        /// <returns></returns>
        public static SecurityToken ReadJwtToken(string accessToken)
        {
            var tokenHandler = new JwtSecurityTokenHandler();
            if (tokenHandler.CanReadToken(accessToken))
            {
                return tokenHandler.ReadJwtToken(accessToken);
            }

            return default;
        }
        #endregion

        #region 获取 JWT Bearer Token
        /// <summary>
        /// 获取 JWT Bearer Token
        /// </summary>
        /// <param name="httpContext"></param>
        /// <param name="headerKey"></param>
        /// <param name="tokenPrefix"></param>
        /// <returns></returns>
        public static string GetJwtBearerToken(DefaultHttpContext httpContext, string headerKey = "Authorization", string tokenPrefix = "Bearer ")
        {
            // 判断请求报文头中是否有 "Authorization" 报文头
            var bearerToken = httpContext.Request.Headers[headerKey].ToString();
            if (string.IsNullOrWhiteSpace(bearerToken)) return default;

            var prefixLenght = tokenPrefix.Length;
            return bearerToken.StartsWith(tokenPrefix, true, null) && bearerToken.Length > prefixLenght ? bearerToken[prefixLenght..] : default;
        }
        #endregion

        #region 生成Token验证参数
        /// <summary>
        /// 生成Token验证参数
        /// </summary>
        /// <param name="jwtSettings"></param>
        /// <returns></returns>
        public static TokenValidationParameters CreateTokenValidationParameters(JWTSettingsOptions jwtSettings)
        {
            return new TokenValidationParameters
            {
                // 验证签发方密钥
                ValidateIssuerSigningKey = jwtSettings.ValidateIssuerSigningKey.Value,
                // 签发方密钥
                IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtSettings.IssuerSigningKey)),
                // 验证签发方
                ValidateIssuer = jwtSettings.ValidateIssuer.Value,
                // 设置签发方
                ValidIssuer = jwtSettings.ValidIssuer,
                // 验证签收方
                ValidateAudience = jwtSettings.ValidateAudience.Value,
                // 设置接收方
                ValidAudience = jwtSettings.ValidAudience,
                // 验证生存期
                ValidateLifetime = jwtSettings.ValidateLifetime.Value,
                // 过期时间容错值
                ClockSkew = TimeSpan.FromSeconds(jwtSettings.ClockSkew.Value),
            };
        }
        #endregion

        #region 设置默认 Jwt 配置
        /// <summary>
        /// 设置默认 Jwt 配置
        /// </summary>
        /// <param name="options"></param>
        /// <returns></returns>
        internal static JWTSettingsOptions SetDefaultJwtSettings(JWTSettingsOptions options)
        {
            options.ValidateIssuerSigningKey ??= true;
            if (options.ValidateIssuerSigningKey == true)
            {
                options.IssuerSigningKey ??= "U2FsdGVkX1+6H3D8Q/QMhInzTdRZI/y9DbUGetbyaag=";
            }
            options.ValidateIssuer ??= true;
            if (options.ValidateIssuer == true)
            {
                options.ValidIssuer ??= "dotnetstarry";
            }
            options.ValidateAudience ??= true;
            if (options.ValidateAudience == true)
            {
                options.ValidAudience ??= "powerby starry";
            }
            options.ValidateLifetime ??= true;
            if (options.ValidateLifetime == true)
            {
                options.ClockSkew ??= 10;
            }
            options.ExpiredTime ??= 20;
            options.Algorithm ??= SecurityAlgorithms.HmacSha256;

            return options;
        }
        #endregion

        #region 固定的 Claim 类型
        /// <summary>
        /// 固定的 Claim 类型
        /// </summary>
        private static readonly string[] StationaryClaimTypes = new[] {
            JwtRegisteredClaimNames.Iat,
            JwtRegisteredClaimNames.Nbf,
            JwtRegisteredClaimNames.Exp,
            JwtRegisteredClaimNames.Iss,
            JwtRegisteredClaimNames.Aud
        };
        #endregion
    }
}
